// Package logger provides structured logging with context
package logger

import (
	"context"
	"io"
	"os"
	"path/filepath"
	"time"

	"github.com/lestrrat-go/file-rotatelogs"
	"github.com/sirupsen/logrus"
)

// Logger interface for structured logging
type Logger interface {
	// Logging methods
	Debug(msg string, fields ...interface{})
	Info(msg string, fields ...interface{})
	Warn(msg string, fields ...interface{})
	Error(msg string, fields ...interface{})
	Fatal(msg string, fields ...interface{})
	Panic(msg string, fields ...interface{})

	// Contextual logging
	WithContext(ctx context.Context) Logger
	WithField(key string, value interface{}) Logger
	WithFields(fields map[string]interface{}) Logger
	WithError(err error) Logger

	// Request logging
	WithRequestID(requestID string) Logger
	WithUserID(userID string) Logger

	// Structured logging
	WithComponent(component string) Logger
	WithOperation(operation string) Logger

	// Logger management
	SetLevel(level string) error
	Close() error
}

// Config holds logger configuration
type Config struct {
	Level      string `json:"level" toml:"level"`
	Path       string `json:"path" toml:"path"`
	Name       string `json:"name" toml:"name"`
	MaxSize    int    `json:"max_size" toml:"max_size"`       // megabytes
	MaxBackups int    `json:"max_backups" toml:"max_backups"`
	MaxAge     int    `json:"max_age" toml:"max_age"`         // days
	Compress   bool   `json:"compress" toml:"compress"`
}

// logrusLogger implements Logger using logrus
type logrusLogger struct {
	*logrus.Entry
	config *Config
}

// NewLogger creates a new structured logger
func NewLogger(config *Config) (Logger, error) {
	logger := logrus.New()

	// Set log level
	level, err := logrus.ParseLevel(config.Level)
	if err != nil {
		level = logrus.InfoLevel
	}
	logger.SetLevel(level)

	// Set JSON formatter for structured logging
	logger.SetFormatter(&logrus.JSONFormatter{
		TimestampFormat: time.RFC3339,
		FieldMap: logrus.FieldMap{
			logrus.FieldKeyTime:  "time",
			logrus.FieldKeyLevel: "level",
			logrus.FieldKeyMsg:   "message",
		},
	})

	// Setup file rotation
	if config.Path != "" {
		// Ensure log directory exists
		if err := os.MkdirAll(config.Path, 0755); err != nil {
			return nil, err
		}

		// Setup log rotation using file-rotatelogs
		logFile := filepath.Join(config.Path, config.Name+".log")
		rotator, err := rotatelogs.New(
			logFile+".%Y%m%d%H%M",
			rotatelogs.WithLinkName(logFile),
			rotatelogs.WithMaxAge(time.Duration(config.MaxAge)*24*time.Hour),
			rotatelogs.WithRotationTime(24*time.Hour),
		)
		if err != nil {
			return nil, err
		}

		// Write to both file and stdout in development
		if level == logrus.DebugLevel {
			logger.SetOutput(io.MultiWriter(os.Stdout, rotator))
		} else {
			logger.SetOutput(rotator)
		}
	} else {
		logger.SetOutput(os.Stdout)
	}

	return &logrusLogger{
		Entry:  logrus.NewEntry(logger),
		config: config,
	}, nil
}

// Debug logs a debug message
func (l *logrusLogger) Debug(msg string, fields ...interface{}) {
	l.Entry.WithFields(parseFields(fields...)).Debug(msg)
}

// Info logs an info message
func (l *logrusLogger) Info(msg string, fields ...interface{}) {
	l.Entry.WithFields(parseFields(fields...)).Info(msg)
}

// Warn logs a warning message
func (l *logrusLogger) Warn(msg string, fields ...interface{}) {
	l.Entry.WithFields(parseFields(fields...)).Warn(msg)
}

// Error logs an error message
func (l *logrusLogger) Error(msg string, fields ...interface{}) {
	l.Entry.WithFields(parseFields(fields...)).Error(msg)
}

// Fatal logs a fatal message and exits
func (l *logrusLogger) Fatal(msg string, fields ...interface{}) {
	l.Entry.WithFields(parseFields(fields...)).Fatal(msg)
}

// Panic logs a panic message and panics
func (l *logrusLogger) Panic(msg string, fields ...interface{}) {
	l.Entry.WithFields(parseFields(fields...)).Panic(msg)
}

// WithContext adds context to the logger
func (l *logrusLogger) WithContext(ctx context.Context) Logger {
	fields := logrus.Fields{}
	
	// Extract common context values
	if requestID := ctx.Value("request_id"); requestID != nil {
		fields["request_id"] = requestID
	}
	if userID := ctx.Value("user_id"); userID != nil {
		fields["user_id"] = userID
	}
	if traceID := ctx.Value("trace_id"); traceID != nil {
		fields["trace_id"] = traceID
	}

	return &logrusLogger{
		Entry:  l.Entry.WithFields(fields),
		config: l.config,
	}
}

// WithField adds a single field to the logger
func (l *logrusLogger) WithField(key string, value interface{}) Logger {
	return &logrusLogger{
		Entry:  l.Entry.WithField(key, value),
		config: l.config,
	}
}

// WithFields adds multiple fields to the logger
func (l *logrusLogger) WithFields(fields map[string]interface{}) Logger {
	return &logrusLogger{
		Entry:  l.Entry.WithFields(logrus.Fields(fields)),
		config: l.config,
	}
}

// WithError adds an error to the logger
func (l *logrusLogger) WithError(err error) Logger {
	return &logrusLogger{
		Entry:  l.Entry.WithError(err),
		config: l.config,
	}
}

// WithRequestID adds request ID to the logger
func (l *logrusLogger) WithRequestID(requestID string) Logger {
	return l.WithField("request_id", requestID)
}

// WithUserID adds user ID to the logger
func (l *logrusLogger) WithUserID(userID string) Logger {
	return l.WithField("user_id", userID)
}

// WithComponent adds component name to the logger
func (l *logrusLogger) WithComponent(component string) Logger {
	return l.WithField("component", component)
}

// WithOperation adds operation name to the logger
func (l *logrusLogger) WithOperation(operation string) Logger {
	return l.WithField("operation", operation)
}

// SetLevel sets the log level
func (l *logrusLogger) SetLevel(level string) error {
	logLevel, err := logrus.ParseLevel(level)
	if err != nil {
		return err
	}
	l.Logger.SetLevel(logLevel)
	return nil
}

// Close closes the logger
func (l *logrusLogger) Close() error {
	// Logrus doesn't need explicit closing, but we might need to close file handles
	return nil
}

// parseFields converts varargs to logrus.Fields
func parseFields(fields ...interface{}) logrus.Fields {
	result := make(logrus.Fields)
	
	for i := 0; i < len(fields)-1; i += 2 {
		if key, ok := fields[i].(string); ok && i+1 < len(fields) {
			result[key] = fields[i+1]
		}
	}
	
	return result
}

// ContextKey type for context keys
type ContextKey string

const (
	// RequestIDKey is the context key for request ID
	RequestIDKey ContextKey = "request_id"
	// UserIDKey is the context key for user ID
	UserIDKey ContextKey = "user_id"
	// TraceIDKey is the context key for trace ID
	TraceIDKey ContextKey = "trace_id"
)

// WithRequestID adds request ID to context
func WithRequestID(ctx context.Context, requestID string) context.Context {
	return context.WithValue(ctx, RequestIDKey, requestID)
}

// WithUserID adds user ID to context
func WithUserID(ctx context.Context, userID string) context.Context {
	return context.WithValue(ctx, UserIDKey, userID)
}

// WithTraceID adds trace ID to context
func WithTraceID(ctx context.Context, traceID string) context.Context {
	return context.WithValue(ctx, TraceIDKey, traceID)
}